package com.rpcdemo;

import com.rpcdemo.proxy.MyProxy;
import com.rpcdemo.rpc.Dispatcher;
import com.rpcdemo.rpc.protocol.MyContent;
import com.rpcdemo.rpc.protocol.MyHeader;
import com.rpcdemo.rpc.transport.MyHttpServlet;
import com.rpcdemo.rpc.transport.ServerDecoder;
import com.rpcdemo.rpc.transport.ServerRequestHandler;
import com.rpcdemo.service.Cat;
import com.rpcdemo.service.MyCat;
import com.rpcdemo.service.Person;
import com.rpcdemo.util.SeriaUtil;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufAllocator;
import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.channel.socket.nio.NioSocketChannel;
import io.netty.handler.codec.http.*;
import org.eclipse.jetty.server.Server;
import org.eclipse.jetty.servlet.ServletContextHandler;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.concurrent.atomic.AtomicInteger;

public class MyRpcTest {

    @Test
    public void server() {

        MyCat cat = new MyCat();

        Dispatcher dispatcher = Dispatcher.getDispatcher();
        dispatcher.register(Cat.class.getName(), cat);

        NioEventLoopGroup eventExecutors = new NioEventLoopGroup(10);
        NioEventLoopGroup worker = eventExecutors;

        ServerBootstrap bs = new ServerBootstrap();
        try {
            bs.group(eventExecutors, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            System.out.println("server rec client port: " + ch.remoteAddress().getPort());
                            ChannelPipeline pipeline = ch.pipeline();
                            pipeline.addLast(new ServerDecoder());
                            pipeline.addLast(new ServerRequestHandler());
                        }
                    }).bind(new InetSocketAddress("localhost", 9090))
                    .sync()
                    .channel()
                    .closeFuture()
                    .sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }

    @Test
    public void nettyHttpServer() {


        MyCat cat = new MyCat();

        Dispatcher dispatcher = Dispatcher.getDispatcher();
        dispatcher.register(Cat.class.getName(), cat);

        NioEventLoopGroup eventExecutors = new NioEventLoopGroup(10);
        NioEventLoopGroup worker = eventExecutors;

        ServerBootstrap bs = new ServerBootstrap();
        try {
            bs.group(eventExecutors, worker)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<NioSocketChannel>() {
                        @Override
                        protected void initChannel(NioSocketChannel ch) throws Exception {
                            System.out.println("server rec client port: " + ch.remoteAddress().getPort());
                            ChannelPipeline pipeline = ch.pipeline();

                            pipeline.addLast(new HttpServerCodec())
                                    .addLast(new HttpObjectAggregator(1024 * 512))
                                    .addLast(new ChannelInboundHandlerAdapter(){
                                        @Override
                                        public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
                                            FullHttpRequest request = (FullHttpRequest) msg;
                                            System.out.println(request.toString());

                                            ByteBuf content = request.content();
                                            byte[] bytes = new byte[content.readableBytes()];
                                            content.readBytes(bytes);
                                            ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(bytes));
                                            MyContent myContent = (MyContent)ois.readObject();


                                            String clazz = myContent.getName();
                                            String methodName = myContent.getMethodName();
                                            Class<?>[] parameterTypes = myContent.getParameterTypes();
                                            Object[] args = myContent.getArgs();

                                            Object impl = dispatcher.get(clazz);

                                            Class<?> implClass = impl.getClass();
                                            Object res = null;
                                            try {
                                                Method method = implClass.getMethod(methodName, parameterTypes);
                                                res = method.invoke(impl, args);

                                            } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
                                                e.printStackTrace();
                                            }

                                            MyContent respBody = new MyContent();
                                            respBody.setRes(res);
                                            byte[] byteBody = SeriaUtil.encode(respBody);

                                            DefaultFullHttpResponse response = new DefaultFullHttpResponse(HttpVersion.HTTP_1_1,
                                                    HttpResponseStatus.OK,
                                                    Unpooled.copiedBuffer(byteBody));

                                            response.headers().add(HttpHeaderNames.CONTENT_LENGTH, byteBody.length);
                                            ctx.writeAndFlush(response);

                                        }
                                    });

                        }
                    }).bind(new InetSocketAddress("localhost", 9090))
                    .sync()
                    .channel()
                    .closeFuture()
                    .sync();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }



    }

    @Test
    public void jettyServer() {


        MyCat cat = new MyCat();

        Dispatcher dispatcher = Dispatcher.getDispatcher();
        dispatcher.register(Cat.class.getName(), cat);

        Server server = new Server(new InetSocketAddress("localhost", 9090));
        ServletContextHandler handler = new ServletContextHandler(server, "/");
        server.setHandler(handler);

        handler.addServlet(MyHttpServlet.class, "/*");

        try {
            server.start();
            server.join();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    /**
     * 模拟客户端
     */
    @Test
    public void app() {

//        new Thread(this::server).start();
//
//        System.out.println("server start...");

        AtomicInteger ind = new AtomicInteger(0);
        Thread[] threads = new Thread[20];
        for (int i = 0; i < 20; i++) {
            threads[i] = new Thread(() -> {
                Cat cat = MyProxy.proxyGet(Cat.class);
                String arg = "hello cat " + ind.incrementAndGet();
                String res = cat.say(arg);
                System.out.println("client over res: " + res + " src arg: " + arg);
            });
        }
        Arrays.stream(threads).forEach(Thread::start);

        try {
            System.in.read();
        } catch (IOException e) {
            e.printStackTrace();
        }


    }

    @Test
    public void testrpc() {

        Cat cat = MyProxy.proxyGet(Cat.class);

        Person person = cat.getPerson("xxx", 12);

        System.out.println(person);


    }

}
